
#region Using Statements
using System;
using System.Collections.Generic;//using System.Xml.Serialization;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion

namespace Pauliver
{

    public abstract class PersistantBaseAI
    {
        enum FiniteStateMachine { PickTarget, MoveTowardsTarget, FindHigherGround, SelectWeapon, Deploy, AimBegin, AimEnd, FireRight, FireLeft, Run, EndTurn };
        
        Team myTeam;
        FiniteStateMachine state;
        protected float NextEventAt = 0;
        FakeGamePad gamepad = null;

        Vector2 direction = Vector2.Zero;
        float distance = 8000;
        PlayerObject Me = null;
        Vector2 TargetPosition = Vector2.Zero;
        Vector2 position;

        public PersistantBaseAI(Team p_myteam)
        {
            myTeam = p_myteam;
        }

        public virtual void ResetTurn(UserTeamPlayerManager UTPM)
        {
            gamepad = (FakeGamePad)PInput.Instance.GamePad(UTPM.CurrentPlayer);
            Me = UTPM.CurrentAvatar;
            Team myteam = Me.Team;
            position = Me.Position();
            state = FiniteStateMachine.PickTarget;
            TargetPosition = Vector2.Zero;

            UserTeamPlayer target;
            {// Pick which team to attack
                LinkedList<UserTeamPlayer> pms = UTPM.PlayerManagers;
                if (pms.First.Value.Team != myteam)
                {
                    target = pms.First.Value;
                }
                else
                {
                    target = pms.Last.Value;
                }
                int currentMax = target.TotalHP();
                foreach (UserTeamPlayer utp in pms)
                {
                    if (utp.TotalHP() >= currentMax && utp.Team != myteam)
                    {
                        target = utp;
                        // Since AI should always be the last player index
                        // AI will attack other AI if all HP are equal
                        // This lets our players "win" more often which players like
                        // to change this make it > currentMax not >= currentMax
                    }
                }
            }
            {// Pick which avatar to attack
                distance = 8000;
                foreach (PlayerObject enemy in target.Avatars)
                {
                    Vector2 enemyposition = enemy.Position();
                    float newdistance = (enemyposition - position).X;
                    if (Math.Abs(newdistance) < Math.Abs(distance))
                    {
                        distance = newdistance;
                        TargetPosition = enemyposition;
                    }
                }
            }
            direction = Vector2.Zero;
        }

        public virtual void PickTarget(float ElapsedTime)
        {
            if (ElapsedTime > 2.0f)
            {
                state = FiniteStateMachine.MoveTowardsTarget;
                NextEventAt = ElapsedTime;
            }
        }
        public virtual void MoveTowardsTarget(float ElapsedTime) 
        {
            const int MaxShotDistance = 375;
            const int MinShot = 32;
            if (distance > MaxShotDistance)
            {
                gamepad.SetLeftStickState(new Vector2(1.0f, 0.0f));
            }
            else if (distance < -MaxShotDistance)
            {
                gamepad.SetLeftStickState(new Vector2(-1.0f, 0.0f));
            }
            else if (distance > -MinShot && distance < MinShot && direction == Vector2.Zero)
            {
                if (distance > 0)
                {
                    if (position.X < 1870)
                    {
                        direction = new Vector2(1.0f, 0.0f);
                        gamepad.SetLeftStickState(direction);
                    }
                    else
                    {
                        direction = new Vector2(-1.0f, 0.0f);
                        gamepad.SetLeftStickState(direction);
                    }

                }
                else if (distance <= 0)
                {
                    if (position.X > 50)
                    {
                        direction = new Vector2(-1.0f, 0.0f);
                        gamepad.SetLeftStickState(direction);
                    }
                    else
                    {
                        direction = new Vector2(1.0f, 0.0f);
                        gamepad.SetLeftStickState(direction);
                    }
                }
            }
            else if (distance > -MinShot && distance < MinShot && direction != Vector2.Zero)
            {
                gamepad.SetLeftStickState(direction);
            }
            else
            {
                state = FiniteStateMachine.FindHigherGround;
                NextEventAt = ElapsedTime + 0.2f;
            }
        }
        public virtual void FindHigherGround(float ElapsedTime)
        {
            if (ElapsedTime > NextEventAt)
            {
                state = FiniteStateMachine.SelectWeapon;
                NextEventAt = ElapsedTime + 0.1f;
            }
            else
            {
                if (distance > 0.0f)
                {
                    gamepad.SetLeftStickState(new Vector2(1.0f, 0.0f));
                }
                else if (distance < 0.0f)
                {
                    gamepad.SetLeftStickState(new Vector2(-1.0f, 0.0f));
                }
            }
        }
        public virtual void SelectWeapon(float ElapsedTime) 
        {
            bool canshoot = ((Me[ComponentType.Holder_Weapon] as Holder_Weapon).Weapon[ComponentType.Input_Shoot] as Input_Shoot).CanShoot;
            //@@ Select a weapon!
            Me[ComponentType.Holder_Weapon] = new Holder_Weapon(new ClusterLauncher(Me));

            ((Me[ComponentType.Holder_Weapon] as Holder_Weapon).Weapon[ComponentType.Input_Shoot] as Input_Shoot).CanShoot = canshoot;
            state = FiniteStateMachine.Deploy;
            NextEventAt = ElapsedTime;
        }
        public virtual void Deploy(float ElapsedTime) 
        {
            if (ElapsedTime > NextEventAt)
            {
                gamepad.SetState(Buttons.A, InputWrapper.ButtonState.Held);
                NextEventAt = ElapsedTime + 0.8f;
                state = FiniteStateMachine.AimBegin;
            }
        }
        public virtual void AimBegin(float ElapsedTime) 
        {
            if (ElapsedTime > NextEventAt)
            {
                NextEventAt = ElapsedTime + 0.2125f;
                state = FiniteStateMachine.AimEnd;
            }
        }
        public virtual void AimEnd(float ElapsedTime) 
        {
            if (ElapsedTime > NextEventAt)
            {
                gamepad.SetState(Buttons.A, InputWrapper.ButtonState.Held);
                NextEventAt = ElapsedTime + 7.5f;
                if (Me.GetFacing() == Facing.Left)
                {
                    state = FiniteStateMachine.FireLeft;
                }
                else if (Me.GetFacing() == Facing.Right)
                {
                    state = FiniteStateMachine.FireRight;
                }
                else
                {
                    //Now what?
                }
            }
            else
            {
                gamepad.SetLeftStickState(new Vector2(0.0f, 1.0f));
            }
        }
        public virtual void FireRight(float ElapsedTime) 
        {
            if (ElapsedTime > NextEventAt)
            {
                gamepad.SetState(Buttons.A, InputWrapper.ButtonState.Held);
                state = FiniteStateMachine.Run;
                NextEventAt = ElapsedTime + 4.0f;
            }
            else
            {
                float distance2 = TargetPosition.X - (Game1.Instance.CurrentViewport.X + (0.5f * Game1.Instance.CurrentViewport.Width));
                if (distance2 > -1.5f)
                {
                    gamepad.SetState(Buttons.A, InputWrapper.ButtonState.Held);
                }
                else
                {
                    state = FiniteStateMachine.Run;
                    NextEventAt = ElapsedTime + 4.0f;
                }
            }
        }
        public virtual void FireLeft(float ElapsedTime) 
        {
            if (ElapsedTime > NextEventAt)
            {
                gamepad.SetState(Buttons.A, InputWrapper.ButtonState.Held);
                state = FiniteStateMachine.Run;
                NextEventAt = ElapsedTime + 4.0f;
            }
            else
            {
                float distance2 = TargetPosition.X - (Game1.Instance.CurrentViewport.X + (0.5f * Game1.Instance.CurrentViewport.Width));
                if (distance2 < 1.5f)
                {
                    gamepad.SetState(Buttons.A, InputWrapper.ButtonState.Held);
                }
                else
                {
                    state = FiniteStateMachine.Run;
                    NextEventAt = ElapsedTime + 4.0f;
                }
            }
        }
        public virtual void Run(float ElapsedTime) 
        {
            if (ElapsedTime > NextEventAt)
            {
                //@@ always move 1 unit to re-center camera
                if (distance > 0)
                {
                    gamepad.SetLeftStickState(new Vector2(1.0f, 0.0f));
                }
                else if (distance < 0)
                {
                    gamepad.SetLeftStickState(new Vector2(-1.0f, 0.0f));
                }
                state = FiniteStateMachine.EndTurn;
                NextEventAt = ElapsedTime;
            }
        }
        public virtual void EndTurn(float ElapsedTime) 
        {
            gamepad.Update();
            gamepad.SetState(Buttons.Back, ButtonWrapper.ButtonState.Pressed);
        }

        public virtual void Update(float ElapsedTime)
        {
            switch (state)
            {
                case FiniteStateMachine.PickTarget:
                    PickTarget(ElapsedTime);
                    break;
                case FiniteStateMachine.MoveTowardsTarget:
                    MoveTowardsTarget(ElapsedTime);
                    break;
                case FiniteStateMachine.FindHigherGround:
                    FindHigherGround(ElapsedTime);
                    break;
                case FiniteStateMachine.SelectWeapon:
                    SelectWeapon(ElapsedTime);
                    break;
                case FiniteStateMachine.Deploy:
                    Deploy(ElapsedTime);
                    break;
                case FiniteStateMachine.AimBegin:
                    AimBegin(ElapsedTime);
                    break;
                case FiniteStateMachine.AimEnd:
                    AimEnd(ElapsedTime);
                    break;
                case FiniteStateMachine.FireRight:
                    FireRight(ElapsedTime);
                    break;
                case FiniteStateMachine.FireLeft:
                    FireLeft(ElapsedTime);
                    break;
                case FiniteStateMachine.Run:
                    Run(ElapsedTime);
                    break;
                case FiniteStateMachine.EndTurn:
                    EndTurn(ElapsedTime);
                    break;
                default:
                    break;
            }
        }
    }

}